﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mathematics.Expressions;

namespace Mathematics
{
    public static class SystemFunctions
    {
        #region Math Formulas

        #region Trigonometric Functions

        public static ExpressionValue tanr(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Tan(parameters[0].NumberValue));
        }

        public static ExpressionValue tand(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Tan(rad(parameters[0].NumberValue)));
        }

        public static ExpressionValue cosr(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Cos(parameters[0].NumberValue));
        }

        public static ExpressionValue cosd(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Cos(rad(parameters[0].NumberValue)));
        }

        public static ExpressionValue sinr(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Sin(parameters[0].NumberValue));
        }

        public static ExpressionValue sind(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(Math.Sin(rad(parameters[0].NumberValue)));
        }

        public static ExpressionValue rad(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(rad(parameters[0].NumberValue));
        }

        public static ExpressionValue deg(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(deg(parameters[0].NumberValue));
        }

        private static double deg(double x)
        {
            return x * (180.0 / Math.PI);
        }

        private static double rad(double x)
        {
            return Math.PI * x / 180.0;
        }

        #endregion

        #region Data Type Functions

        public static ExpressionValue type(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewString(parameters[0].TypeName);
        }

        public static ExpressionValue isnumber(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].Type==ValueTypes.Number);
        }

        public static ExpressionValue isbool(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].Type == ValueTypes.Bool);
        }

        public static ExpressionValue isstring(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].Type == ValueTypes.String);
        }

        public static ExpressionValue isvoid(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].Type == ValueTypes.Void);
        }

        public static ExpressionValue istrue(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].BoolValue);
        }

        public static ExpressionValue isfalse(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(!parameters[0].BoolValue);
        }

        public static ExpressionValue tonumber(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(parameters[0].NumberValue);
        }

        public static ExpressionValue tobool(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewBool(parameters[0].BoolValue);
        }

        public static ExpressionValue tostring(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewString(parameters[0].StringValue);
        }

        #endregion

        #region String Functions

        public static ExpressionValue length(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewNumber(parameters[0].StringValue.Length);
        }

        public static ExpressionValue reverse(params ExpressionValue[] parameters)
        {
            return ExpressionValue.NewString(new String(parameters[0].StringValue.Reverse().ToArray()));
        }

        #endregion

        #region Introspective Function Definitions

        public static ExpressionValue domains(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var d in domain.AllDomains)
            {
                builder.AppendLine(d.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        public static ExpressionValue domainsl(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var d in domain.Domains)
            {
                builder.AppendLine(d.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        public static ExpressionValue functions(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var f in domain.AllFunctions)
            {
                builder.AppendLine(f.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        public static ExpressionValue functionsl(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var f in domain.Functions)
            {
                builder.AppendLine(f.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        public static ExpressionValue variables(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var v in domain.AllVariables)
            {
                builder.AppendLine(v.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        public static ExpressionValue variablesl(params ExpressionValue[] parameters)
        {
            StringBuilder builder;
            var domain = FindDomain(parameters, out builder);

            foreach (var v in domain.Variables)
            {
                builder.AppendLine(v.Name);
            }

            return ExpressionValue.NewString(builder.ToString());
        }

        private static Domain FindDomain(ExpressionValue[] parameters, out StringBuilder builder)
        {
            Domain domain = null;
            builder = new StringBuilder();

            var domainName = parameters[0].StringValue;
            if (string.IsNullOrEmpty(domainName))
                domain = Domain.CurrentDomain;
            else if (domainName == "_")
                domain = Domain.RootDomain;
            else
            {
                domain = Domain.RootDomain.AllDomains.SingleOrDefault(d => d.Name == domainName);
                if (domain == null)
                    throw new InvalidOperationException(string.Format("No domain named '{0}' was found.", domainName));
            }

            return domain;
        }

        #endregion

        #endregion

        #region System Function Definitions

        #region Trigonometrics

        public static readonly FunctionDefinition Sinr = new SystemFunctionDefinition("sinr",sinr, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Sin = new SystemFunctionDefinition("sin", sind, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Cosr = new SystemFunctionDefinition("cosr", cosr, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Cos = new SystemFunctionDefinition("cos", cosd, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Tanr = new SystemFunctionDefinition("tanr", tanr, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Tan = new SystemFunctionDefinition("tan", tand, new ParameterDefinition("x"));
        public static readonly FunctionDefinition Rad = new SystemFunctionDefinition("rad", rad, new ParameterDefinition("deg"));
        public static readonly FunctionDefinition Deg = new SystemFunctionDefinition("deg", deg, new ParameterDefinition("rad"));

        #endregion 

        #region Data Type Functions

        public static readonly FunctionDefinition Type = new SystemFunctionDefinition("type", type, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsNumber = new SystemFunctionDefinition("isnumber", isnumber, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsBool = new SystemFunctionDefinition("isbool", isbool, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsTrue = new SystemFunctionDefinition("istrue", istrue, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsFalse = new SystemFunctionDefinition("isfalse", isfalse, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsString = new SystemFunctionDefinition("isstring", isstring, new ParameterDefinition("value"));
        public static readonly FunctionDefinition ToNumber = new SystemFunctionDefinition("tonumber", tonumber, new ParameterDefinition("value"));
        public static readonly FunctionDefinition ToBool = new SystemFunctionDefinition("tobool", tobool, new ParameterDefinition("value"));
        public static readonly new FunctionDefinition ToString = new SystemFunctionDefinition("tostring", tostring, new ParameterDefinition("value"));
        public static readonly FunctionDefinition IsVoid = new SystemFunctionDefinition("isvoid", isvoid, new ParameterDefinition("value"));

        #endregion

        #region String Functions

        public static readonly FunctionDefinition Length = new SystemFunctionDefinition("length", length, new ParameterDefinition("str"));
        public static readonly FunctionDefinition Reverse = new SystemFunctionDefinition("reverse", reverse, new ParameterDefinition("str"));

        #endregion

        #region Introspective Functions

        public static readonly FunctionDefinition Domains = new SystemFunctionDefinition("domains", domains, new ParameterDefinition("domain",ExpressionValue.EmptyString));
        public static readonly FunctionDefinition DomainsL = new SystemFunctionDefinition("domainsl", domainsl, new ParameterDefinition("domain", ExpressionValue.EmptyString));
        public static readonly FunctionDefinition Functions = new SystemFunctionDefinition("functions", functions, new ParameterDefinition("domain", ExpressionValue.EmptyString));
        public static readonly FunctionDefinition FunctionsL = new SystemFunctionDefinition("functionsl", functionsl, new ParameterDefinition("domain", ExpressionValue.EmptyString));
        public static readonly FunctionDefinition Variables = new SystemFunctionDefinition("variables", variables, new ParameterDefinition("domain", ExpressionValue.EmptyString));
        public static readonly FunctionDefinition VariablesL = new SystemFunctionDefinition("variablesl", domains, new ParameterDefinition("domain", ExpressionValue.EmptyString));

        #endregion

        #endregion
    }
}
